﻿namespace OasisCiq.XS
{
    using System;
    using System.Xml;

    public class XSTime
    {
        private const long MaxOffset = TimeSpan.TicksPerHour * 14;
        private const long MinOffset = -MaxOffset;

        private const long MaxTime = TimeSpan.TicksPerDay - 1;
        private const long MinTime = 0;

        private TimeSpan time;
        private TimeSpan offset;

        public XSTime()
        {
        }

        public XSTime(TimeSpan value)
            : this(value, TimeSpan.Zero)
        {
        }

        public XSTime(TimeSpan value, TimeSpan offset)
        {
            Time = value;
            Offset = offset;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException">
        ///     With inner <see cref="ArgumentOutOfRangeException"/> or <see cref="FormatException"/>
        /// </exception>
        public XSTime(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException();
            }

            try
            {
                var dto = XmlConvert.ToDateTimeOffset(value, new[] { "HH:mm:ssK", "HH:mm:ss.FFFFFFK" });
                Time = dto.TimeOfDay;
                Offset = dto.Offset;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new ArgumentOutOfRangeException(ex.Message, ex);
            }
            catch (FormatException ex)
            {
                throw new ArgumentException(ex.Message, ex);
            }
        }

        public TimeSpan Time
        {
            get
            {
                return time;
            }

            set
            {
                if (value.Ticks < MinTime || value.Ticks > MaxTime)
                {
                    throw new ArgumentOutOfRangeException();
                }

                time = value;
            }
        }

        public TimeSpan Offset
        {
            get
            {
                return offset;
            }

            set
            {
                if (value.Ticks < MinOffset || value.Ticks > MaxOffset)
                {
                    throw new ArgumentOutOfRangeException();
                }

                offset = value;
            }
        }

        public TimeSpan UtcTime
        {
            get { return Time.Add(Offset); }
        }

        public override string ToString()
        {
            if (Offset == TimeSpan.Zero)
            {
                var dt = new DateTime(0, DateTimeKind.Utc).Add(Time);
                return XmlConvert.ToString(dt, "HH:mm:ss.FFFFFFK");
            }

            var dto = new DateTimeOffset(
                new DateTime(0, DateTimeKind.Unspecified).Add(Time),
                Offset);
            return XmlConvert.ToString(dto, "HH:mm:ss.FFFFFFK");
        }
    }
}